#include "BTreeNode.h"
#include <string.h>

using namespace std;

const int MY_ERROR   = -1015;

// Convert buffer to string
char* BTLeafNode::toString(){
	char *result = (char*)malloc(256*sizeof(char));
	memset(result, 0, 256);
	LeafPageData *lpd = (LeafPageData*)this->buffer;
	sprintf(result, "pid: %d \n", lpd->id); 
	
	for (int i = 0; i < MAX_KEY_COUNT; i++){
		char temp[128];
		LeafPageFileRecord lpfr = lpd->data[i];
		if (lpfr.key == -1 && lpfr.id.pid == -1 && lpfr.id.sid == -1){
			continue;
		}
		sprintf(temp, "pid: %d sid: %d key %d\n", lpfr.id.pid, lpfr.id.sid, lpfr.key);
		result = strcat(result, temp);
	}
	return result;
}

// Print out the buffer
void BTLeafNode::printBuffer(){
	printf("This is the buffer: \n%s", toString());
	fflush(0);
}

// Convert buffer to string
char* BTNonLeafNode::toString(){
	char *result = (char*)malloc(256*sizeof(char));
	memset(result, 0, 256);
	NonLeafPageData *nlpd = (NonLeafPageData*)this->buffer;
	sprintf(result, "pid: %d \n", nlpd->id);  
	
	for (int i = 0; i < MAX_KEY_COUNT; i++){
		char temp[128];
		NonLeafPageFileRecord nlpfr = nlpd->data[i];
		if (nlpfr.key == -1 && nlpfr.id == -1){
			continue;
		}
		sprintf(temp, "key: %d pid %d\n", nlpfr.key, nlpfr.id);
		result = strcat(result, temp);
	}
	return result;
}

// Print out the buffer
void BTNonLeafNode::printBuffer(){
	printf("This is the buffer: \n%s", toString());
	fflush(0);
}



/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::read(PageId pid, const PageFile& pf)
{ 
	return pf.read(pid, buffer); // Will return 0 if successful, otherwise will return the error returned in pf
}
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::write(PageId pid, PageFile& pf)
{
	return pf.write(pid, buffer); // Will return 0 if successful, otherwise will return the error returned in pf
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTLeafNode::getKeyCount()
{
	int count = 0;
	LeafPageData *lpd = (LeafPageData*)this->buffer;
	for (int i = 0; i < MAX_KEY_COUNT; i++){
		LeafPageFileRecord lpfr = lpd->data[i];
		if (lpfr.key == -1 && lpfr.id.pid == -1 && lpfr.id.sid == -1){
			continue;
		}
		count++;
	}
	return count;
}

/*
 * Insert a (key, rid) pair to the node.
 * @param key[IN] the key to insert
 * @param rid[IN] the RecordId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTLeafNode::insert(int key, const RecordId& rid) 
{
	if(key == -1){ // Overwriting ROOT info to the leaf in first page, not regular insert!
		LeafPageData *mlpd = (LeafPageData*)this->buffer;
		mlpd->data[0].key = key;
		mlpd->data[0].id = rid;
		if(key == -1 && rid.sid == -1 && rid.pid == -1) { return MY_ERROR; }
		return 0;
	}
	
	if(getKeyCount() == MAX_KEY_COUNT){ // If buffer is full
		return MY_ERROR;
	}
	
	LeafPageData *lpd = (LeafPageData*)this->buffer;
	for (int i = 0; i < MAX_KEY_COUNT; i++){
		LeafPageFileRecord lpfr = lpd->data[i];
		if (lpd->data[i].key == -1 && lpfr.id.pid == -1 && lpfr.id.sid == -1){ // Insert after all keys
			lpd->data[i].key = key;
			lpd->data[i].id = rid;
			break;
		}
		if (lpfr.key > key){ // Insert in between other keys
			int move = PageFile::PAGE_SIZE - ((i+1)*BT_TUPLE_SIZE) - sizeof(PageId);
			memmove(&(lpd->data[i+1]), &(lpd->data[i]), move);
			lpd->data[i].key = key;
			lpd->data[i].id = rid;
			break;
		}
	}
	return 0;
}

/*
 * Insert the (key, rid) pair to the node
 * and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @param siblingKey[OUT] the first key in the sibling node after split.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::insertAndSplit(int key, const RecordId& rid, 
                              BTLeafNode& sibling, int& siblingKey)
{	
/*
Inside the insertAndSplit function, you do not need to know the pageid for 
the sibling node. You can assume the function that calls insertAndSplit() 
knows the pageid for the sibling node, and calls setNextNodePtr() to set 
that pageid .
*/
	LeafPageData *lpd = (LeafPageData*)this->buffer;
	LeafPageData *siblpd = (LeafPageData*)(&sibling)->buffer;

	// Insert all second half into sibling
	int move = PageFile::PAGE_SIZE - ((MAX_KEY_COUNT/2)*BT_TUPLE_SIZE) - sizeof(PageId);
	memmove(&(siblpd->data[0]), &(lpd->data[MAX_KEY_COUNT/2]), move);
	memset(&(lpd->data[MAX_KEY_COUNT/2]), -1, move); // Get rid of 2nd half of nodes from this
	siblingKey = siblpd->data[0].key;
	
	for (int i = 0; i < (MAX_KEY_COUNT/2); i++){ // Always takes floor, MAX/2 = 85/2 = 42 leaving 43 in sib
		LeafPageFileRecord lpfr = lpd->data[i];
		if (key < lpfr.key){ // Insert in between other keys
			// Insert this node in correct place
			return (this->insert(key,rid)); // Returns 0 if successful
		}
	}
	
	//Since did not return, key should be inserted in sibling node.
	return ((&sibling)->insert(key,rid));
}

/*
 * Find the entry whose key value is larger than or equal to searchKey
 * and output the eid (entry number) whose key value >= searchKey.
 * Remeber that all keys inside a B+tree node should be kept sorted.
 * @param searchKey[IN] the key to search for
 * @param eid[OUT] the entry number that contains a key larger than or equalty to searchKey
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::locate(int searchKey, int& eid)
{
	LeafPageData *lpd = (LeafPageData*)this->buffer;
	for (int i = 0; i < MAX_KEY_COUNT; i++){
		LeafPageFileRecord lpfr = lpd->data[i];
		if (lpfr.key == -1 && lpfr.id.pid == -1 && lpfr.id.sid == -1){ // Insert after all keys
			return MY_ERROR; // There are no values >= searchKey in this leaf node
		}
		if (lpfr.key >= searchKey){ // output eid
			eid = i; // Set entry number
			return 0;
		}
	}
	return MY_ERROR;
}

/*
 * Read the (key, rid) pair from the eid entry.
 * @param eid[IN] the entry number to read the (key, rid) pair from
 * @param key[OUT] the key from the entry
 * @param rid[OUT] the RecordId from the entry
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::readEntry(int eid, int& key, RecordId& rid)
{
	LeafPageData *lpd = (LeafPageData*)this->buffer;
	LeafPageFileRecord lpfr = lpd->data[eid];
	key = lpfr.key;
	rid = lpfr.id;
	if(key == -1 && rid.sid == -1 && rid.pid == -1) { return MY_ERROR; }
	return 0;
}

/*
 * Return the pid of the next slibling node.
 * @return the PageId of the next sibling node 
 */
PageId BTLeafNode::getNextNodePtr()
{
	LeafPageData *lpd = (LeafPageData*)this->buffer;
	return lpd->id;
}

/*
 * Set the pid of the next slibling node.
 * @param pid[IN] the PageId of the next sibling node 
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::setNextNodePtr(PageId pid)
{
	LeafPageData *lpd = (LeafPageData*)this->buffer;
	lpd->id = pid;
	return 0;
}


/* Possible Mistakes

1. Checking buffer on condition that incorrect is -1 (since set in constructor, but when read it goes poof)
      this affects getKeyCount
2. When does setNextNodePtr get an error? error handling in readEntry
3. CHECK ALL ERROR CASES BELOW :(

*/












/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::read(PageId pid, const PageFile& pf)
{
	return pf.read(pid, buffer); // Will return 0 if successful, otherwise will return the error returned in pf
}
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::write(PageId pid, PageFile& pf)
{
	return pf.write(pid, buffer); // Will return 0 if successful, otherwise will return the error returned in pf
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTNonLeafNode::getKeyCount()
{
	int count = 0;
	NonLeafPageData *nlpd = (NonLeafPageData*)this->buffer;
	for (int i = 0; i < MAX_KEY_COUNT; i++){
		NonLeafPageFileRecord nlpfr = nlpd->data[i];
		if (nlpfr.key == -1 && nlpfr.id == -1){
			continue;
		}
		count++;
	}
	return count;
}


/*
 * Insert a (key, pid) pair to the node.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTNonLeafNode::insert(int key, PageId pid)
{
	if(getKeyCount() == MAX_KEY_COUNT){ // If buffer is full
		return MY_ERROR;
	}
	
	NonLeafPageData *nlpd = (NonLeafPageData*)this->buffer;
	for (int i = 0; i < MAX_KEY_COUNT; i++){
		NonLeafPageFileRecord nlpfr = nlpd->data[i];
		if (nlpfr.key == -1 && nlpfr.id == -1){ // Insert after all keys
			nlpd->data[i].key = key;
			nlpd->data[i].id = pid;
			break;
		}
		if (nlpfr.key > key){ // Insert in between other keys
			int move = PageFile::PAGE_SIZE - ((i+1)*BT_TUPLE_SIZE) - sizeof(PageId);
			memmove(&(nlpd->data[i+1]), &(nlpd->data[i]), move);
			nlpd->data[i].key = key;
			nlpd->data[i].id = pid;
			break;
		}
	}
	return 0;
}

/*
 * Insert the (key, pid) pair to the node
 * and split the node half and half with sibling.
 * The middle key after the split is returned in midKey.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @param sibling[IN] the sibling node to split with. This node MUST be empty when this function is called.
 * @param midKey[OUT] the key in the middle after the split. This key should be inserted to the parent node.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::insertAndSplit(int key, PageId pid, BTNonLeafNode& sibling, int& midKey)
{
	// This function deletes midKey from the first entry of sibling node *************************
	// midkey needs to be added to the parent, as well as a pointer to sibling in the pageid of parent
	NonLeafPageData *nlpd = (NonLeafPageData*)this->buffer;
	NonLeafPageData *sibnlpd = (NonLeafPageData*)(&sibling)->buffer;
	
	// Have 127 tuples. Split to 63 here, 63 in sibling, (the mid key pid is first in sibling, and id is midkey)
	// After this, insert.

	// Insert all second half into sibling
	int move = PageFile::PAGE_SIZE - (((MAX_KEY_COUNT/2)+1)*BT_TUPLE_SIZE) - sizeof(PageId); //move 63 tuples
	midKey = nlpd->data[(MAX_KEY_COUNT/2)+1].key; // Take from first node of sibling
	sibnlpd->id = nlpd->data[(MAX_KEY_COUNT/2)+1].id; // Since we get rid of midKey, return the int, make sure the pageid goes in front of sibling
	
	memmove(&(sibnlpd->data[0]), &(nlpd->data[(MAX_KEY_COUNT/2)+1]), move);
	memset(&(nlpd->data[(MAX_KEY_COUNT/2+1)]), -1, move+1); // Get rid of 64 tuples
	
	for (int i = 0; i <= (MAX_KEY_COUNT/2); i++){ // Always takes floor, 1020/8 = MAX = 127, 127/2 = 63 leaving 64 in sib
		NonLeafPageFileRecord nlpfr = nlpd->data[i];
		if (key < nlpfr.key){ // Insert in between other keys
			// Insert this node in correct place
			return (this->insert(key,pid)); // Returns 0 if successful
		}
	}
	
	//Since did not return, key should be inserted in sibling node.
	return ((&sibling)->insert(key,pid));
}

/*
 * Given the searchKey, find the child-node pointer to follow and
 * output it in pid.
 * @param searchKey[IN] the searchKey that is being looked up.
 * @param pid[OUT] the pointer to the child node to follow.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::locateChildPtr(int searchKey, PageId& pid)
{
	NonLeafPageData *nlpd = (NonLeafPageData*)this->buffer;
	// If it is less than the first key
	if(searchKey < nlpd->data[0].key){
		pid = nlpd->id;
		return 0; // What if error? How is there an error?
	}
	for (int i = 0; i < MAX_KEY_COUNT; i++){
		NonLeafPageFileRecord nlpfr = nlpd->data[i];
		if (i == 0 && nlpfr.key == -1 && nlpfr.id == -1){ // Error, cant be empty
			return MY_ERROR; // ERROR
		}else if ((nlpfr.key == -1 && nlpfr.id == -1) || (searchKey < nlpfr.key)){ // Get the last pid
			if(i == 0){ pid = nlpd->id; }
			else{ pid = nlpd->data[i-1].id; }
			return 0;
		}else if (nlpfr.key == searchKey){
			pid = nlpd->data[i].id;
			return 0;
		}else{ // nlpfr.key > searchKey
			if( i == (MAX_KEY_COUNT - 1) ){
				pid = nlpd->data[i].id; // This is the last pid, our search key is greater than all keys
				return 0;
			}
			continue; // keep going
		}
	}
	return MY_ERROR;
}

/*
 * Initialize the root node with (pid1, key, pid2).
 * @param pid1[IN] the first PageId to insert
 * @param key[IN] the key that should be inserted between the two PageIds
 * @param pid2[IN] the PageId to insert behind the key
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::initializeRoot(PageId pid1, int key, PageId pid2)
{
	NonLeafPageData *nlpd = (NonLeafPageData*)this->buffer;
	nlpd->id = pid1;
	nlpd->data[0].key = key;
	nlpd->data[0].id = pid2;
	if(getKeyCount() == 1){ return 0; }
	else{ return MY_ERROR; } // This root isn't empty before initialization
}
